package gui_interface;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;

public class Movie extends JPanel {
	// prend le fichier xml des actions et le transforme en série d'images
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	List<Element> actions; // list of steps
	int index; // number of steps already drawn

	boolean displaysGradients = true;
	// true if it displays gradients, false if it displays subgradients

	BufferedImage imageGradient;
	BufferedImage imageSubGradient;

	int largeur;
	int hauteur;
	int cote;
	int font;
	int largeur_carre;
	int debut_carre;

	public Movie(String configFile, String movie) throws FileNotFoundException {

		setupImage(configFile);
		try {
			SAXBuilder sxb = new SAXBuilder();
			Document document = sxb.build(new File(movie));
			Element racine = document.getRootElement();
			actions = racine.getChildren("AddNode");
			index = 0;
			addRocks(racine);

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public void add(int number_of_steps) {
		// avance de N pas dans la séquence , 1 pas = 1 ajout de noeud
		Graphics2D g1 = imageGradient.createGraphics();
		Graphics2D g2 = imageSubGradient.createGraphics();
		for (int i = 0; i < number_of_steps; i++) {
			if (index < actions.size() & index >= 0) {
				Element current_action = actions.get(index);
				// on ajoute le noeud
				add(g1, current_action);
				add(g2, current_action);
				// on met a jour tous les gradients
				for (Element e : current_action.getChildren("ChangeGradient")) {
					addGradient(g1, e);
				}
				for (Element e : current_action
						.getChildren("ChangeSubGradient")) {
					addGradient(g2, e);
				}
				// on met a jour le type
				for (Element e : current_action.getChildren("ChangeType")) {
					addType(g1, e);
					addType(g2, e);
				}
				index++;
			}
		}
		// g.drawImage(theImage,0,0,this);
	}

	public void addRocks(Element racine) {
		Graphics2D g1 = imageGradient.createGraphics();
		Graphics2D g2 = imageSubGradient.createGraphics();
		List<Element> rocks = racine.getChildren("AddRock");
		for (Element e : rocks) {
			int i = Integer.parseInt(e.getAttributeValue("x"));
			int j = Integer.parseInt(e.getAttributeValue("y"));
			fillRectangle(g1, i, j, rockColor());
			fillRectangle(g2, i, j, rockColor());
		}
	}

	public void remove(int number_of_steps) {
		// contrary action of add
		Graphics2D g1 = imageGradient.createGraphics();
		Graphics2D g2 = imageSubGradient.createGraphics();

		for (int i = 0; i < number_of_steps; i++) {
			if (index <= actions.size() & index > 0) {
				index--;
				Element current_action = actions.get(index);

				for (Element e : current_action.getChildren("ChangeType")) {
					removeType(g1, e);
					removeType(g2, e);
				}
				for (Element e : current_action.getChildren("ChangeGradient")) {
					removeGradient(g1, e);
				}
				for (Element e : current_action
						.getChildren("ChangeSubGradient")) {
					removeGradient(g2, e);
				}
				remove(g1, current_action);
				remove(g2, current_action);

			}

		}
		// g.drawImage(theImage,0,0,this);
	}

	@Override
	public void paintComponent(Graphics g) {
		if (displaysGradients) {
			g.drawImage(imageGradient, 0, 0, null);
		} else {
			g.drawImage(imageSubGradient, 0, 0, null);
		}
	}

	private void setupImage(String configFile) {
		Config config = new Config(configFile);
		Dimension tailleEcran = java.awt.Toolkit.getDefaultToolkit()
				.getScreenSize();
		hauteur = (int) tailleEcran.getHeight();
		largeur = hauteur * 3 / 2;
		largeur_carre = hauteur / 2;
		debut_carre = hauteur;
		// largeur = (int)tailleEcran.getWidth()*2/5;
		// hauteur = largeur;
		int size_environment = config.size_environment;

		font = hauteur / size_environment / 2;
		cote = font / 2;

		imageGradient = new BufferedImage(largeur, hauteur,
				BufferedImage.TYPE_INT_RGB);
		Graphics2D g = imageGradient.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g.setColor(backgroundColor());
		g.fillRect(0, 0, largeur, hauteur);
		g.dispose();

		imageSubGradient = new BufferedImage(largeur, hauteur,
				BufferedImage.TYPE_INT_RGB);
		Graphics2D g1 = imageSubGradient.createGraphics();
		g1.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g1.setColor(backgroundColor());
		g1.fillRect(0, 0, largeur, hauteur);
		g1.dispose();

	}

	private void add(Graphics2D g, Element e) {
		// add the chosen node to the image
		// colored square

		int i = Integer.parseInt(e.getAttributeValue("x"));
		int j = Integer.parseInt(e.getAttributeValue("y"));
		fillRectangle(g, i, j,
				colorFromType(Integer.parseInt(e.getAttributeValue("color"))));

		// colored gradients

		drawNumber(g, i, j, "top", e.getAttributeValue("topG"),
				colorFromDirection("top"));
		drawNumber(g, i, j, "bot", e.getAttributeValue("botG"),
				colorFromDirection("bot"));
		drawNumber(g, i, j, "right", e.getAttributeValue("rightG"),
				colorFromDirection("right"));
		drawNumber(g, i, j, "left", e.getAttributeValue("leftG"),
				colorFromDirection("left"));

	}

	private void addGradient(Graphics2D g, Element e) {
		// change the gradient as expressed in the element e
		int i = Integer.parseInt(e.getAttributeValue("x"));
		int j = Integer.parseInt(e.getAttributeValue("y"));

		// remove the old gradient,
		drawNumber(g, i, j, e.getAttributeValue("dir"),
				e.getAttributeValue("old"), backgroundColor());

		// add the new one
		drawNumber(g, i, j, e.getAttributeValue("dir"),
				e.getAttributeValue("new"),
				colorFromDirection(e.getAttributeValue("dir")));

	}

	private void addType(Graphics2D g, Element e) {
		// change the gradient as expressed in the element e
		int i = Integer.parseInt(e.getAttributeValue("x"));
		int j = Integer.parseInt(e.getAttributeValue("y"));
		int type = Integer.parseInt(e.getAttributeValue("new"));
		fillRectangle(g, i, j, colorFromType(type));

	}

	private void remove(Graphics2D g, Element e) {
		int i = Integer.parseInt(e.getAttributeValue("x"));
		int j = Integer.parseInt(e.getAttributeValue("y"));
		fillRectangle(g, i, j, backgroundColor());
		drawNumber(g, i, j, "top", e.getAttributeValue("topG"),
				backgroundColor());
		drawNumber(g, i, j, "bot", e.getAttributeValue("botG"),
				backgroundColor());
		drawNumber(g, i, j, "right", e.getAttributeValue("rightG"),
				backgroundColor());
		drawNumber(g, i, j, "left", e.getAttributeValue("leftG"),
				backgroundColor());

	}

	private void removeGradient(Graphics2D g, Element e) {
		int i = Integer.parseInt(e.getAttributeValue("x"));
		int j = Integer.parseInt(e.getAttributeValue("y"));

		// remove the new gradient,
		drawNumber(g, i, j, e.getAttributeValue("dir"),
				e.getAttributeValue("new"), backgroundColor());

		// add the old one
		drawNumber(g, i, j, e.getAttributeValue("dir"),
				e.getAttributeValue("old"),
				colorFromDirection(e.getAttributeValue("dir")));

	}

	private void removeType(Graphics2D g, Element e) {
		// change the gradient as expressed in the element e
		int i = Integer.parseInt(e.getAttributeValue("x"));
		int j = Integer.parseInt(e.getAttributeValue("y"));
		int type = Integer.parseInt(e.getAttributeValue("old"));
		fillRectangle(g, i, j, colorFromType(type));

	}

	private void fillRectangle(Graphics2D g, int x, int y, Color c) {
		g.setColor(c);
		g.fillRect(debut_carre + x * cote, largeur_carre - y * cote, cote, cote);
	}

	private void drawNumber(Graphics2D g, int i, int j, String direction,
			String gradient, Color c) {

		if (direction.equals("top")) {
			g.setFont(new Font(null, Font.CENTER_BASELINE, font));
			g.setColor(c);
			g.drawString(gradient, i * font * 3, hauteur - j * font * 3 - font);
		}
		if (direction.equals("bot")) {
			g.setFont(new Font(null, Font.CENTER_BASELINE, font));
			g.setColor(c);
			g.drawString(gradient, i * font * 3, hauteur - j * font * 3 + font);
		}
		if (direction.equals("right")) {
			g.setFont(new Font(null, Font.CENTER_BASELINE, font));
			g.setColor(c);
			g.drawString(gradient, i * font * 3 + font, hauteur - j * font * 3);
		}
		if (direction.equals("left")) {
			g.setFont(new Font(null, Font.CENTER_BASELINE, font));
			g.setColor(c);
			g.drawString(gradient, i * font * 3 - font, hauteur - j * font * 3);
		}

	}

	private Color colorFromType(int type) {
		// decides the colors of the nodes according to type
		switch (type) {
		case 0:
			return Color.yellow;
		case 1:
			return Color.green;
		case 2:
			return Color.red;
		case 3:
			return Color.blue;
		default:
			return Color.black;
		}
	}

	private Color colorFromDirection(String dir) {
		// decides the colors of the gradients
		if (dir.equals("top")) {
			return Color.BLUE;
		}
		if (dir.equals("bot")) {
			return Color.RED;
		}
		if (dir.equals("right")) {
			return Color.GREEN;
		}
		if (dir.equals("left")) {
			return Color.PINK;
		}
		return Color.BLACK;
	}

	private Color backgroundColor() {
		return Color.BLACK;
	}

	private Color rockColor() {
		return Color.GRAY;
	}

	public void switchView() {
		displaysGradients = !displaysGradients;
	}

}
